package ${domain}.frame.schedule;

import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
import org.springframework.util.ErrorHandler;
import ${domain}.frame.utils.LogUtil;
import ${domain}.frame.auth.LocalData;
import ${domain}.module.wsys.mgr.LogerrManager;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ScheduledFuture;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * 调度器
 *
 * @author wangbing
 * @version 0.0.1
 * @since 2020-01-01
 */
public class Scheduler extends ThreadPoolTaskScheduler implements ErrorHandler {

    private Map<String, TaskWrapper> taskMap = new HashMap<>();

    public Scheduler() {
        setErrorHandler(this);
        setPoolSize(4);
        initialize();
    }

    public boolean createOrRepeat(RunTask task) {
        if (taskMap.containsKey(task.taskId())) {
            ScheduledFuture<?> scheduledFuture = taskMap.get(task.taskId()).future;
            scheduledFuture.cancel(false);
        }
        taskMap.put(task.taskId(), new TaskWrapper(task));
        return true;
    }

    public boolean remove(String taskId) {
        if (taskMap.containsKey(taskId)) {
            ScheduledFuture<?> scheduledFuture = taskMap.get(taskId).future;
            scheduledFuture.cancel(false);
            taskMap.remove(taskId);
        }
        return true;
    }

    public boolean start(String taskId) {
        if (taskMap.containsKey(taskId)) {
            taskMap.get(taskId).run = true;
            taskMap.get(taskId).future = taskMap.get(taskId).target.schedule(this);
            return true;
        }
        return false;
    }

    public boolean stop(String taskId) {
        if (taskMap.containsKey(taskId)) {
            taskMap.get(taskId).run = false;
            ScheduledFuture<?> scheduledFuture = taskMap.get(taskId).future;
            scheduledFuture.cancel(false);
            return true;
        }
        return false;
    }

    public List<Schedule> taskList() {
        List<Schedule> result = new ArrayList<>();
        for (TaskWrapper taskWrapper : taskMap.values()) {
            Schedule schedule = new Schedule();
            schedule.setId(taskWrapper.taskId);
            schedule.setName(taskWrapper.taskName);
            schedule.setNote(taskWrapper.taskNote);
            schedule.setRun(taskWrapper.run);
            result.add(schedule);
        }
        return result;
    }

    /**
     * 【此方法为异常最终处理】通常是线程执行任务时未捕捉的的异常。
     * @param throwable
     */
    @Override
    public void handleError(Throwable throwable) {
        String title = throwable.getMessage();
        String message = LogUtil.getTrace(throwable);
        LogUtil.e(message);
        LogerrManager logerrManager = LocalData.getBean(LogerrManager.class);
        if (logerrManager != null)
            logerrManager.addErr("任务错误", title, message);
    }

    class TaskWrapper {
        RunTask target;
        String taskId;
        String taskName;
        String taskNote;
        boolean run;
        ScheduledFuture<?> future;

        public TaskWrapper(RunTask runTask) {
            this.target = runTask;
            this.taskId = runTask.taskId();
            this.taskName = runTask.taskName();
            this.taskNote = runTask.taskNote();
            this.future = runTask.schedule(Scheduler.this);
            this.run = true;
        }
    }

    public static class Schedule {
        private String id;
        private String name;
        private String note;
        private boolean run;

        public String getId() {
            return id;
        }

        public void setId(String id) {
            this.id = id;
        }

        public String getName() {
            return name;
        }

        public void setName(String name) {
            this.name = name;
        }

        public String getNote() {
            return note;
        }

        public void setNote(String note) {
            this.note = note;
        }

        public boolean isRun() {
            return run;
        }

        public void setRun(boolean run) {
            this.run = run;
        }
    }
}